/*
 * Copyright 2004 original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jbfilter.jsp;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;

import org.apache.commons.lang.StringUtils;
import org.jbfilter.core.Filter;
import org.jbfilter.core.FilterComponentContainer;
import org.jbfilter.core.factory.FilterProviderRegistry;

public class FilterTag<E>
	extends
		JbfilterSimpleTagSupport
	implements
		FilterComponentContainerTag<E> {

	private String id;// mandatory
	private Filter<E> filter;
	private FilterDelegate<E> filterDelegate;// optionally set by child
	private String varComponents;// optionally to make EL expression shorter
	private Boolean clean;// if true triggers cleaning of all the filter components
	private ProviderSupport providerSupport = new ProviderSupport();
	
	private List<FilterTagCallable> callableKids;
	
	@Override
	public void doTag() throws JspException, IOException {
		super.doTag();
		filter = retrieveFilter();
		if (varComponents != null) {
			setPageScopeAttribute(varComponents, filter.getFilterComponentsById());
		}
		// invoke the body
		JspFragment body = getJspBody();
		if (body != null) {
			body.invoke(null);
		}
		if (isCleanCommandExecuted()) {
			filter.clean();
		}
		processDoTagCallbacks();
		if (filterDelegate != null) {
			filterDelegate.filter(getJspContext());
		}
	}
	
	private void processDoTagCallbacks() throws JspException {
		if(callableKids == null) {
			return;
		}
		for (FilterTagCallable callable : callableKids) {
			callable.doTagCallback();
		}
	}
	@Override
	public void registerFilterTagCallable(FilterTagCallable callable) {
		if(callableKids == null) {
			callableKids = new ArrayList<FilterTagCallable>();
		}
		callableKids.add(callable);
	}
	@Override
	public FilterComponentContainer<E> getFilterComponentContainer() {
		return filter;
	}
	// retrieves the filter from the session or creates new one
	private Filter<E> retrieveFilter() throws JspException {
		Object fromSession = getFromSession(id);
		if (fromSession == null) {
			Filter<E> filter = createFilter();
			storeInSession(id, filter);
			return filter;
		} 
		if (! (fromSession instanceof Filter)) {
			throw new ClassCastException(String.format("Expected class: %s, actual class: %s.", Filter.class, fromSession.getClass()));
		}
		@SuppressWarnings("unchecked")
		Filter<E> f = (Filter<E>)fromSession;
		return f;
	}

	public Filter<E> createFilter() throws JspException {
		String providerName = getProviderName();
		String providerClassName = getProviderClassName();
		if (StringUtils.isNotBlank(providerName)) {
			return FilterProviderRegistry.getInstance().fromName(providerName).newFilter();
		} else if (StringUtils.isNotBlank(providerClassName)) {
			return FilterProviderRegistry.getInstance().fromClassName(providerClassName).newFilter();
		} else {
			throw new JspException(String.format("%s or %s must be set in tag %s.",
					JbfilterTagAttribute.providerName,
					JbfilterTagAttribute.providerClassName,
					JbfilterTag.filter));
		}
	}
	
	public void setId(String id) {
		this.id = id;
	}
	
	public Filter<E> getFilter() {
		return filter;
	}
	public void setFilterDelegate(FilterDelegate<E> filterDelegate) {
		this.filterDelegate = filterDelegate;
	}
	public void setVarComponents(String varComponents) {
		this.varComponents = varComponents;
	}

	/**
	 * Any string that is not empty or {@code null} will switch on the clean flag, so that
	 * the {@link #isCleanCommandExecuted()} returns {@code true}.
	 * @param anyString
	 */
	public void setClean(String anyString) {
		clean = StringUtils.isNotBlank(anyString);
	}

	//Clean command was executed.
	private boolean isCleanCommandExecuted() {
		return clean != null && clean.booleanValue();
	}
	public void setProviderName(String providerName) {
		providerSupport.setProviderName(providerName);
	}
	public void setProviderClassName(String providerClassName) {
		providerSupport.setProviderClassName(providerClassName);
	}
	@Override
	public String getProviderName() {
		return providerSupport.getProviderName();
	}
	@Override
	public String getProviderClassName() {
		return providerSupport.getProviderClassName();
	}
	
}
